/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#include <glib-object.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>

#include <thrift/c_glib/thrift.h>
#include <thrift/c_glib/protocol/thrift_binary_protocol_factory.h>
#include <thrift/c_glib/protocol/thrift_protocol_factory.h>
#include <thrift/c_glib/server/thrift_server.h>
#include <thrift/c_glib/server/thrift_simple_server.h>
#include <thrift/c_glib/transport/thrift_buffered_transport_factory.h>
#include <thrift/c_glib/transport/thrift_server_socket.h>
#include <thrift/c_glib/transport/thrift_server_transport.h>

#include "gen-c_glib/dmm.h"

#ifdef  __cplusplus
extern "C" {
#endif

/* In the C (GLib) implementation of Thrift, the actual work done by a
   server---that is, the code that runs when a client invokes a
   service method---is defined in a separate "handler" class that
   implements the service interface. Here we define the
   IdmsDMManagerHandler class, which implements the dmmIf
   interface and provides the behavior expected by tutorial clients.
   (Typically this code would be placed in its own module but for
   clarity this tutorial is presented entirely in a single file.)

   For each service the Thrift compiler generates an abstract base
   class from which handler implementations should inherit. In our
   case IdmsDMManagerHandler inherits from DMManagerHandler,
   defined in gen-c_glib/calculator.h.

   If you're new to GObject, try not to be intimidated by the quantity
   of code here---much of it is boilerplate and can mostly be
   copied-and-pasted from existing work. For more information refer to
   the GObject Reference Manual, available online at
   https://developer.gnome.org/gobject/. */

#define TYPE_IDMS_DMM_HANDLER \
  (idms_dmmanager_handler_get_type ())

#define IDMS_DMM_HANDLER(obj)                                \
  (G_TYPE_CHECK_INSTANCE_CAST ((obj),                                   \
                               TYPE_IDMS_DMM_HANDLER,        \
                               IdmsDMManagerHandler))
#define IDMS_DMM_HANDLER_CLASS(c)                    \
  (G_TYPE_CHECK_CLASS_CAST ((c),                                \
                            TYPE_IDMS_DMM_HANDLER,   \
                            IdmsDMManagerHandlerClass))
#define IS_IDMS_DMM_HANDLER(obj)                             \
  (G_TYPE_CHECK_INSTANCE_TYPE ((obj),                                   \
                               TYPE_IDMS_DMM_HANDLER))
#define IS_IDMS_DMM_HANDLER_CLASS(c)                 \
  (G_TYPE_CHECK_CLASS_TYPE ((c),                                \
                            TYPE_IDMS_DMM_HANDLER))
#define IDMS_DMM_HANDLER_GET_CLASS(obj)              \
  (G_TYPE_INSTANCE_GET_CLASS ((obj),                            \
                              TYPE_IDMS_DMM_HANDLER, \
                              IdmsDMManagerHandlerClass))

struct _IdmsDMManagerHandler {
     dmmHandler parent_instance;

  /* private */
  GHashTable *log;
};
typedef struct _IdmsDMManagerHandler IdmsDMManagerHandler;

struct _IdmsDMManagerHandlerClass {
  dmmHandlerClass parent_class;
};
typedef struct _IdmsDMManagerHandlerClass IdmsDMManagerHandlerClass;

GType idms_dmmanager_handler_get_type (void);

#ifdef  __cplusplus
}
#endif

/* ---------------------------------------------------------------- */

/* The implementation of IdmsDMManagerHandler follows. */

G_DEFINE_TYPE (IdmsDMManagerHandler,
               idms_dmmanager_handler,
               TYPE_DMM_HANDLER)

/* Each of a handler's methods accepts at least two parameters: A
   pointer to the service-interface implementation (the handler object
   itself) and a handle to a GError structure to receive information
   about any error that occurs.

   On success, a handler method returns TRUE. A return value of FALSE
   indicates an error occurred and the error parameter has been
   set. (Methods should not return FALSE without first setting the
   error parameter.) */
static gboolean
idms_dmmanager_handler_initialize (dmmIf  *iface, gint32 *_return, GError **error)
{
  THRIFT_UNUSED_VAR (iface);
  THRIFT_UNUSED_VAR (error);

  puts ("S:init");
  *_return = 0;
  return TRUE;
}

static gboolean
idms_dmmanager_handler_create_tree (dmmIf  *iface, gint32 *_return, GError **error)
{
  THRIFT_UNUSED_VAR (iface);
  THRIFT_UNUSED_VAR (error);

  puts ("S:create tree");
  *_return = 0;
  return TRUE;
}


/* Service-method parameters are passed through as parameters to the
   handler method.

   If the service method returns a value an output parameter, _return,
   is additionally passed to the handler method. This parameter should
   be set appropriately before the method returns, whenever it
   succeeds.

   The return value from this method happens to be of a base type,
   i32, but note if a method returns a complex type such as a map or
   list *_return will point to a pre-allocated data structure that
   does not need to be re-allocated and should not be destroyed. */
static char * gName;
static char * gKey;
static char * gVal;
static gboolean idms_dmmanager_handler_get_para_value (dmmIf  *iface, ParameterValue_t **_return, GError **error)
{
    THRIFT_UNUSED_VAR (iface);  
    THRIFT_UNUSED_VAR (error);

    g_object_set (*_return,
                "name", gName,
                "paramkey",   gKey,
                "val",    gVal, NULL);
                
    return TRUE;
}

static gboolean idms_dmmanager_handler_set_para_value (dmmIf *iface, gint32* _return, const ParameterValue_t *para_value, GError **error)
{
    THRIFT_UNUSED_VAR (iface);
    THRIFT_UNUSED_VAR (error);

    printf("S set:name:[%s], paramKey:[%s], val:[%s]\n", para_value->name, para_value->paramkey, para_value->val);

    g_object_get ((ParameterValue_t *)para_value,
                "name", &gName,
                "paramkey",   &gKey,
                "val",    &gVal, NULL);
                
    *_return = 0;
    return TRUE;
}

static gboolean hello_world_handler_respone_to_client_1 (dmmIf *iface, GPtrArray ** _return, const GPtrArray * send, GError **error)
{
    hello_world_t *param1;
    hello_world_t *param2;

    THRIFT_UNUSED_VAR (iface);
    THRIFT_UNUSED_VAR (error);

    //*_return = g_ptr_array_new();

    param1 = g_object_new(TYPE_HELLO_WORLD_T, "response", "get", NULL);
    param2 = g_object_new(TYPE_HELLO_WORLD_T, "response", "have", NULL);

    g_ptr_array_add(*_return, param1);
    g_ptr_array_add(*_return, param2);
    printf("****get message!!****\n");

    return TRUE;
}


/* IdmsDMManagerHandler's instance finalizer (destructor) */
static void
idms_dmmanager_handler_finalize (GObject *object)
{
  IdmsDMManagerHandler *self =
    IDMS_DMM_HANDLER (object);

  /* Free our calculation-log hash table */
  g_hash_table_unref (self->log);
  self->log = NULL;

  /* Chain up to the parent class */
  G_OBJECT_CLASS (idms_dmmanager_handler_parent_class)->finalize (object);
}

/* IdmsDMManagerHandler's instance initializer (constructor) */
static void
idms_dmmanager_handler_init (IdmsDMManagerHandler *self)
{
  /* Create our calculation-log hash table */
  self->log = g_hash_table_new_full (g_int_hash,
                                     g_int_equal,
                                     g_free,
                                     g_object_unref);
}

/* IdmsDMManagerHandler's class initializer */
static void
idms_dmmanager_handler_class_init (IdmsDMManagerHandlerClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  dmmHandlerClass *calculator_handler_class =
    DMM_HANDLER_CLASS (klass);

  /* Register our destructor */
  gobject_class->finalize = idms_dmmanager_handler_finalize;

  /* Register our implementations of DMManagerHandler's methods */
  calculator_handler_class->initialize=
    idms_dmmanager_handler_initialize;
  calculator_handler_class->create_tree=
    idms_dmmanager_handler_create_tree;
  calculator_handler_class->set_parameter_value=
    idms_dmmanager_handler_set_para_value;
  calculator_handler_class->get_parameter_value=
    idms_dmmanager_handler_get_para_value;
  calculator_handler_class->gptrarray_test = hello_world_handler_respone_to_client_1;
}

/* ---------------------------------------------------------------- */

/* That ends the implementation of IdmsDMManagerHandler.
   Everything below is fairly generic code that sets up a minimal
   Thrift server for tutorial clients. */


/* Our server object, declared globally so it is accessible within the
   SIGINT signal handler */
ThriftServer *server = NULL;

/* A flag that indicates whether the server was interrupted with
   SIGINT (i.e. Ctrl-C) so we can tell whether its termination was
   abnormal */
gboolean sigint_received = FALSE;

/* Handle SIGINT ("Ctrl-C") signals by gracefully stopping the
   server */
static void
sigint_handler (int signal_number)
{
  THRIFT_UNUSED_VAR (signal_number);

  /* Take note we were called */
  sigint_received = TRUE;

  /* Shut down the server gracefully */
  if (server != NULL)
    thrift_server_stop (server);
}

int main (void)
{
  IdmsDMManagerHandler *handler;
  dmmProcessor *processor;

  ThriftServerTransport *server_transport;
  ThriftTransportFactory *transport_factory;
  ThriftProtocolFactory *protocol_factory;

  struct sigaction sigint_action;

  GError *error = NULL;
  int exit_status = 0;

#if (!GLIB_CHECK_VERSION (2, 36, 0))
  g_type_init ();
#endif

  /* Create an instance of our handler, which provides the service's
     methods' implementation */
  handler =
    g_object_new (TYPE_IDMS_DMM_HANDLER,
                  NULL);

  /* Create an instance of the service's processor, automatically
     generated by the Thrift compiler, which parses incoming messages
     and dispatches them to the appropriate method in the handler */
  processor =
    g_object_new (TYPE_DMM_PROCESSOR,
                  "handler", handler,
                  NULL);

  /* Create our server socket, which binds to the specified port and
     listens for client connections */
  server_transport =
    g_object_new (THRIFT_TYPE_SERVER_SOCKET,
                  "port", 9010,
                  NULL);

  /* Create our transport factory, used by the server to wrap "raw"
     incoming connections from the client (in this case with a
     ThriftBufferedTransport to improve performance) */
  transport_factory =
    g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT_FACTORY,
                  NULL);

  /* Create our protocol factory, which determines which wire protocol
     the server will use (in this case, Thrift's binary protocol) */
  protocol_factory =
    g_object_new (THRIFT_TYPE_BINARY_PROTOCOL_FACTORY,
                  NULL);

  /* Create the server itself */
  server =
    g_object_new (THRIFT_TYPE_SIMPLE_SERVER,
                  "processor",                processor,
                  "server_transport",         server_transport,
                  "input_transport_factory",  transport_factory,
                  "output_transport_factory", transport_factory,
                  "input_protocol_factory",   protocol_factory,
                  "output_protocol_factory",  protocol_factory,
                  NULL);

  /* Install our SIGINT handler, which handles Ctrl-C being pressed by
     stopping the server gracefully (not strictly necessary, but a
     nice touch) */
  memset (&sigint_action, 0, sizeof (sigint_action));
  sigint_action.sa_handler = sigint_handler;
  sigint_action.sa_flags = SA_RESETHAND;
  sigaction (SIGINT, &sigint_action, NULL);

  /* Start the server, which will run until its stop method is invoked
     (from within the SIGINT handler, in this case) */
  puts ("Starting the server...");
  thrift_server_serve (server, &error);

  /* If the server stopped for any reason other than having been
     interrupted by the user, report the error */
  if (!sigint_received) {
    g_message ("thrift_server_serve: %s",
               error != NULL ? error->message : "(null)");
    g_clear_error (&error);
  }

  puts ("done.");

  g_object_unref (server);
  g_object_unref (transport_factory);
  g_object_unref (protocol_factory);
  g_object_unref (server_transport);

  g_object_unref (processor);
  g_object_unref (handler);

  return exit_status;
}

